package com.cloud.job.snail;

import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cloud.api.domain.job.VinLive;
import com.cloud.job.config.VehicleOnlineConfig;
import com.cloud.job.config.WebSocketServer;
import com.cloud.job.service.IVinLiveService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author 杨斌
 * 2025-03-2025-03-06
 */
@Component
@Slf4j
public class VehicleOnlineStatistics {

    @Resource
    private IVinLiveService vinLiveService;

    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private VehicleOnlineConfig vehicleOnlineConfig;

    @JobExecutor(name = "vehicle_online_statistics")
    @Transactional(rollbackFor = Exception.class)
    public void getVehicleOnlineStatistics() {
        try {
            // 1. 从 Redis 获取在线车辆数据
            List<VinLive> onlineVehicles = fetchOnlineVehiclesFromRedis();

            // 2. 发送 WebSocket 在线数量消息
            sendWebSocketMessage(onlineVehicles);

            // 2.5 发送 WebSocket 今日上线数量消息（新增）
            sendTodayOnlineVehicleCount();

            // 3. 清除历史数据并保存新数据
            saveOrUpdateVehicleData(onlineVehicles);

        } catch (Exception e) {
            log.error("执行 vehicle_online_statistics 任务时发生异常", e);
            throw e; // 抛出异常以确保事务回滚
        }
    }

    private List<VinLive> fetchOnlineVehiclesFromRedis() {
        try {
            String onlineEvVinKey = vehicleOnlineConfig.getVinEvKey();
            long currentTime = System.currentTimeMillis();
            long threeMinutesAgo = currentTime - vehicleOnlineConfig.getTimeWindow();

            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Set<ZSetOperations.TypedTuple<Object>> onlineVins = zSetOps.rangeByScoreWithScores(onlineEvVinKey, threeMinutesAgo, currentTime);

            List<VinLive> onlineVehicles = new ArrayList<>();
            if (onlineVins != null && !onlineVins.isEmpty()) {
                for (ZSetOperations.TypedTuple<Object> tuple : onlineVins) {
                    try {
                        VinLive vinLive = new VinLive();
                        vinLive.setVin((String) tuple.getValue());

                        Double score = tuple.getScore();
                        if (score != null) {
                            LocalDateTime createTime = LocalDateTime.ofInstant(
                                    Instant.ofEpochMilli(score.longValue()),
                                    ZoneId.systemDefault()
                            );
                            vinLive.setCreateTime(createTime);
                            onlineVehicles.add(vinLive);
                        } else {
                            log.warn("Tuple score is null, skipping this record: {}", tuple.getValue());
                        }
                    } catch (ClassCastException | NullPointerException e) {
                        log.error("解析单个 Tuple 时发生异常：tuple={}", tuple, e);
                    }
                }
            }

            return onlineVehicles;

        } catch (Exception e) {
            log.error("从 Redis 获取在线车辆数据时发生异常", e);
            throw e;
        }
    }

    private void sendWebSocketMessage(List<VinLive> onlineVehicles) {
        try {
            int onlineVehicleCount = onlineVehicles.size();
            String message = String.format("{\"onLineEvNum\":\"%d\"}", onlineVehicleCount);
            log.info("在线数: {}", message);

            WebSocketServer.sendMessageToAllUser(message);
        } catch (IOException e) {
            log.error("发送 WebSocket 消息失败", e);
        } catch (Exception e) {
            log.error("发送 WebSocket 消息时发生未知异常", e);
            throw e;
        }
    }

    private void saveOrUpdateVehicleData(List<VinLive> onlineVehicles) {
        try {
            QueryWrapper<VinLive> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("1", "1");
            boolean clearSuccess = vinLiveService.remove(queryWrapper);

            if (!clearSuccess) {
                log.error("清空历史数据失败");
                return;
            }

            if (!onlineVehicles.isEmpty()) {
                boolean saveResult = vinLiveService.saveBatch(onlineVehicles);
                if (saveResult) {
                    log.info("成功存入 {} 条数据到 vin_live 表", onlineVehicles.size());
                } else {
                    log.warn("批量插入数据部分失败");
                }
            } else {
                log.info("No online vehicles to save.");
            }

        } catch (Exception e) {
            log.error("操作数据库时发生异常", e);
            throw e;
        }
    }

    /**
     * 统计今天 00:00 之后上线的车辆数量并发送 WebSocket 消息
     */
    public void sendTodayOnlineVehicleCount() {
        try {
            String onlineEvVinKey = vehicleOnlineConfig.getVinEvKey();

            if (onlineEvVinKey == null || onlineEvVinKey.isEmpty()) {
                log.warn("Redis key 为空，无法统计今日在线车辆数");
                return;
            }

            ZoneId zoneId = ZoneId.of("Asia/Shanghai");
            LocalDateTime todayStart = LocalDateTime.now(zoneId).with(LocalTime.MIN);
            long todayStartTimeMillis = todayStart.atZone(zoneId).toInstant().toEpochMilli();

            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();

            // 统计所有大于等于 todayStartTimeMillis 的记录数量
            Long count = zSetOps.count(onlineEvVinKey, todayStartTimeMillis, Double.MAX_VALUE);

            int todayOnlineCount = count == null ? 0 : count.intValue();

            // 发送 WebSocket 消息
            sendWebSocketMessageForTodayCount(todayOnlineCount);

        } catch (Exception e) {
            log.error("统计今日在线车辆数量时发生异常", e);
//            throw new RuntimeException("统计今日在线车辆数量失败", e);
        }
    }


    /**
     * 发送 WebSocket 消息（今日上线数）
     */
    private void sendWebSocketMessageForTodayCount(int todayOnlineCount) {
        try {
            String message = String.format("{\"todayOnlineVehicleCount\":\"%d\"}", todayOnlineCount);
            log.info("今日上线车辆数: {}", message);
            WebSocketServer.sendMessageToAllUser(message);
        } catch (IOException e) {
            log.error("发送今日上线车辆 WebSocket 消息失败", e);
        } catch (Exception e) {
            log.error("发送今日上线车辆 WebSocket 消息时发生未知异常", e);
//            throw e;
        }
    }
}
